<?php
namespace Tlf;
trait Assertions {
protected $inverted = false;
public function __call($method, $args){
if (!function_exists($method)){
throw new \Exception("'$method' is not a valid assertion, but you can use any existing function as an assertion if it returns truthy/falsey values.");
}
$didPass = $method(...$args);
$this->handleDidPass($didPass);
$c = count($args);
echo "'$method'([$c arguments]) ";
}
/**
* Invert test passage so failing tests actually passes them
*/
public function invert(){
$this->inverted = !$this->inverted;
return $this->inverted;
}
public function isInstanceOf($object, $shouldBe){
$class = get_class($object);
if ($object instanceof $shouldBe){
$this->handleDidPass(true);
echo "'$class' is instanceof '$shouldBe'";
} else {
$this->handleDidPass(false);
echo "'$class' is NOT instanceof '$shouldBe'";
}
}
public function is_object($object){
if (is_object($object)){
$this->handleDidPass(true);
echo "Is an object";
} else {
$this->handleDidPass(false);
echo "Is not an object";
}
}
public function is_false($value){
$this->handleDidPass($value === false);
echo "is_false()";
}
public function is_true($value){
$this->handleDidPass($value === true);
echo "is_true()";
}
public function str_contains($str, $target){
if (is_array($target)){
$success = true;
foreach ($target as $strTarget){
$oneSuccess = $this->str_contains($str,$strTarget);
if (!$oneSuccess)$success = false;
}
return $success;
}
$pass = false;
if (strpos($str,$target)!==false)$pass=true;
$this->handleDidPass($pass);
if ($pass){
echo "String contains '$target'";
} else {
echo "String does not contain '$target'";
}
return false;
}
public function str_not_contains(string $str, $target){
if (is_array($target)){
$success = true;
foreach ($target as $strTarget){
$oneSuccess = $this->str_not_contains($str,$strTarget);
if (!$oneSuccess)$success = false;
}
return $success;
} else $target = (string)$target;
$pass = true;
if (strpos($str,$target)!==false)$pass=false;
$this->handleDidPass($pass);
if ($pass){
echo "String does not contain '$target'";
} else {
echo "String contains '$target'";
}
return false;
}
public function catch($exceptionClass,$strict=false){
$catcher = new Tester\ExceptionCatcher($exceptionClass);
$this->catchers[] = $catcher;
return $catcher;
}
public function throw($e){
$list = $this->catchers;
foreach ($list as $index => $cat){
if ($cat->matches($e)){
//@TODO allow one catcher to be re-used & just require that each exception be caught by at least one.
unset($this->catchers[$index]);
ob_start();
$this->handleDidPass(true);
ob_get_clean();
// "Exception was caught";
return true;
}
}
$this->handleDidPass(false);
echo "An exception was not handled...";
throw $e;
}
public function compare($target, $actual,$strict=false){
if (!$strict&&is_string($target)&&substr($target,0,7)=='file://'){
$file = substr($target,7);
if (!is_file($file)){
throw new \Exception("{$target} is not a file. ");
}
$ext = substr($file,-4);
if ($ext=='.php'){
ob_start();
require($file);
$target=ob_get_clean();
}
else $target=file_get_contents($file);
}
if (!$strict&&is_string($actual)&&substr($actual,0,7)=='file://'){
$file = substr($actual,7);
if (!is_file($file)){
throw new \Exception("{$actual} is not a file. ");
}
$ext = substr($file,-4);
if ($ext=='.php'){
ob_start();
require($file);
$actual=ob_get_clean();
}
else $actual=file_get_contents($file);
}
if (!$strict&&is_string($target)&&is_string($actual)){
$target = trim($target);
$actual = trim($actual);
}
$pass = false;
if ($strict&&($target===$actual))$pass = true;
else if (!$strict&&$target==$actual)$pass = true;
$target = $this->comparisonOutput($target);
$actual = $this->comparisonOutput($actual);
// echo "Strict: ".($strict ? 'true' : 'false');
$this->handleDidPass($pass);
if ($strict)echo " strict comparison";
$this->targetVsActualOutput($target, $actual);
return $pass;
}
/**
* Simply compare the values
* @param $target the value you want
* @param $actual the value you have
* @param $strict TRUE to use `===`. FALSE to use `==`
*/
public function compare_raw($target, $actual, $strict=false){
$pass = $strict ? $target === $actual
: $target == $actual;
ob_start();
var_dump($target);
$target_str = substr(ob_get_clean(), 0,500);
ob_start();
var_dump($actual);
$actual_str = substr(ob_get_clean(), 0,500);
$this->handleDidPass($pass);
$this->targetVsActualOutput(
$target_str,
$actual_str
);
return $pass;
}
public function handleDidPass(bool $didPass){
$extra='';
if ($this->inverted){
$didPass = !$didPass;
$extra=" (inverted)";
}
echo "\n\n";
if ($didPass)echo "+++pass+++$extra";
else echo "---fail---$extra";
echo "\n";
$passStr = $didPass ? 'true' : 'false';
$this->comparisons[$passStr]++;
return $didPass;
}
public function targetVsActualOutput($target, $actual){
// echo "\n";
echo "Target:\n{$target}";
echo "\n--\n";
echo "Actual:\n{$actual}";
echo "\n--------\n";
}
public function comparisonOutput($value){
if (is_object($value)){
return "Object of class ".get_class($value);
}
if (is_array($value)){
array_walk_recursive($value, function(&$value){
$value = str_replace(["\r","\n"],['\r','\n'], $value);
});
$pr = var_export($value, true);
$maxLen= 1000;
if (($this->options['prettyPrintArray'][0]??null)=='true'){
$oneLine = $pr;
$maxLen = 1500;
} else {
$oneLine = implode(" ",array_map('trim',explode("\n",$pr)));
$oneLine = str_replace('\\\\n', '\n', $oneLine);
}
$value = substr($oneLine,0,$maxLen);
if (strlen($oneLine)>$maxLen)$value .= '...';
return $value;
}
if ($value===true)return 'true';
else if ($value===false)return 'false';
return $value;
}
}